package com.flx.sys.jwt;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.flx.sys.exception.CustomException;
import com.flx.sys.jwt.dto.JwtUser;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;

@Component
public class JwtTokenUtil implements Serializable {

	private static final long serialVersionUID = 5768635861389981713L;

	/**
	 */
	private static final String JWT_USERNAME = "flx";
	/**
	 * token生成时间
	 */
	private static final String JWT_CREATEDATE = "create";

	/**
	 * 过期时间天
	 */
	@Value("${jwt.token.expiration}")
	private int expiration;

	/**
	 * 密钥
	 */
	@Value("${jwt.token.secret")
	private String secret;

	/**
	 * 获取用户名
	 * @param token
	 * @return
	 * 2018年4月19日
	 */
	public String getUsernameFromToken(String token) {
		String username;
		try {
			final Claims claims = getClaimsFromToken(token);
			username = claims.getSubject();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
		return username;
	}

	/**
	 * 获取token生成时间
	 * @param token
	 * @return
	 * 2018年4月19日
	 */
	public Date getCreatedDateFromToken(String token) {
		Date created;
		try {
			final Claims claims = getClaimsFromToken(token);
			created = new Date((Long) claims.get(JWT_CREATEDATE));
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
		return created;
	}

	/**
	 * 获取过期时间
	 * @param token
	 * @return
	 * 2018年4月19日
	 */
	public Date getExpirationDateFromToken(String token) {
		Date expiration;
		try {
			final Claims claims = getClaimsFromToken(token);
			expiration = claims.getExpiration();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
		return expiration;
	}

	/**简析jwt
	 * @param token
	 * @return
	 * 2018年4月19日
	 * @throws Exception 
	 */
	private Claims getClaimsFromToken(String token) throws Exception {
		Claims claims;
		try {
			claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
		} catch (SignatureException e) {
			throw new RuntimeException("jwt 解析错误");
		} catch (ExpiredJwtException e) {
			// "jwt 已经过期，在设置jwt的时候如果设置了过期时间，这里会自动判断jwt是否已经过期，如果过期则会抛出这个异常，我们可以抓住这个异常并作相关处理。");
			throw new RuntimeException("jwt 已经过期");
		} catch (MalformedJwtException e) {
			throw new RuntimeException("jwt 格式错误");
		} catch (UnsupportedJwtException e) {
			throw new RuntimeException("不支持该jwt");
		} catch (IllegalArgumentException e) {
			throw new RuntimeException("参数错误异常");
		}
		return claims;
	}

	/**设置过期时间
	 * @param day
	 * @return
	 * 2018年4月19日
	 */
	private Date generateExpirationDate(int day) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		calendar.add(Calendar.DAY_OF_YEAR, day);//当前时间加1天
		//calendar.add(Calendar.MINUTE, 1); // 当前时间加1分钟
		Date date = calendar.getTime();
		return date;
	}

	/**判断过期时间是否大于当前时间
	 * @param token
	 * @return
	 * 2018年4月19日
	 */
	private Boolean isTokenExpired(String token) {
		final Date expiration = getExpirationDateFromToken(token);
		return expiration.before(new Date());
	}

	/**token创建时间是否大于最后一次密码修改时间
	 * @param created
	 * @param lastPasswordReset
	 * @return
	 * 2018年4月19日
	 */
	private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
		return (lastPasswordReset != null && created.before(lastPasswordReset));
	}

	/**生成token
	 * @param userDetails
	 * @return
	 * 2018年4月19日
	 */
	public String generateToken(UserDetails userDetails) {
		Map<String, Object> claims = new HashMap<>();
		claims.put(JWT_USERNAME, userDetails.getUsername());
		claims.put(JWT_CREATEDATE, new Date());
		return generateToken(claims, userDetails.getUsername());
	}

	String generateToken(Map<String, Object> claims, String subject) {
		return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date()) // iat: jwt的签发时间
				.setExpiration(generateExpirationDate(expiration))// 设置过期时间，过期时间要大于签发时间
				.setAudience("zhangmin") // 签名
				.compressWith(CompressionCodecs.DEFLATE) // 压缩生成的内容
				.signWith(SignatureAlgorithm.HS512, secret).compact();
	}

	public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
		final Date created = getCreatedDateFromToken(token);
		return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset) && !isTokenExpired(token);
	}

	/**刷新token
	 * @param token
	 * @return
	 * 2018年4月19日
	 */
	public String refreshToken(String token) {
		String refreshedToken;
		try {
			final Claims claims = getClaimsFromToken(token);
			claims.put(JWT_CREATEDATE, new Date());
			refreshedToken = generateToken(claims, claims.getSubject());
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
		return refreshedToken;
	}

	public Boolean validateToken(String token, UserDetails userDetails) {
		JwtUser user = (JwtUser) userDetails;
		final String username = getUsernameFromToken(token);
		final Date created = getCreatedDateFromToken(token);
		return (username.equals(user.getUsername()) && !isTokenExpired(token)
				&& !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate()));
	}
}
